package whf.framework.xml;

import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Element;
import org.jdom.output.XMLOutputter;

import whf.framework.entity.Entity;
import whf.framework.exception.PropertyException;
import whf.framework.meta.MetaManager;
import whf.framework.service.Service;
import whf.framework.util.BeanFactory;
import whf.framework.util.BeanUtils;
import whf.framework.util.DateUtils;
import whf.framework.util.StringUtils;
import whf.framework.util.TypeUtils;
import whf.framework.util.Utils;
import whf.framework.web.struts.AbstractStrutsForm;

/**
 * xml工具，将对象系列化为xml元素
 * @author wanghaifeng
 *
 */
@SuppressWarnings("unchecked")
public class XmlUtils {
	
	/**
	 * 将jdom转换为对象
	 * @param objectType 转变的对象类型
	 * @param properties 需要获取的属性
	 * @param xmlElement 输入的xml元素
	 * @param dependencyAsProperty false：对于many-to-one对象，使用节点属性表示关联对象；true：使用子节点表示关联对象
	 * @return 将xml转变为对象后的对象
	 * @throws Exception
	 */
	public static Object loadFromXmlElement(Class objectType, String[] properties, Element xmlElement, 
			boolean dependencyAsProperty) throws Exception{
		Object result = objectType.newInstance();
		for(String property: properties){
			PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(objectType, property);
			Class propertyType = pd.getPropertyType();
			String stringPropertyValue = xmlElement.getAttributeValue(property);
			if(propertyType == Long.class || propertyType == long.class){
				BeanUtils.setProperty(result, property, new Long(Long.parseLong(stringPropertyValue)));
			} else if(propertyType == Integer.class || propertyType == int.class){
				BeanUtils.setProperty(result, property, new Integer(Integer.parseInt(stringPropertyValue)));
			} else if(propertyType == java.sql.Date.class || propertyType == java.sql.Timestamp.class || propertyType == java.util.Date.class) {
				if(!StringUtils.isEmpty(stringPropertyValue)){
					Object propertyValue = null;
					if(propertyType == java.sql.Date.class){
						propertyValue = DateUtils.parseDate(stringPropertyValue);
					} else if(propertyType == java.sql.Timestamp.class){
						propertyValue = DateUtils.parseDatetime(stringPropertyValue);
					} else {
						propertyValue = DateUtils.parseDate(stringPropertyValue);
					}
					BeanUtils.setProperty(result, property, propertyValue);
				}
			} else if(propertyType == Boolean.class || propertyType == boolean.class) {
				Boolean b = new Boolean("true".equalsIgnoreCase(stringPropertyValue));
				BeanUtils.setProperty(result, property, b);
			} else if(TypeUtils.isImplementsFrom(propertyType, Entity.class)){
				if(!dependencyAsProperty){
					stringPropertyValue = xmlElement.getChildText(property);
				}
				if(!StringUtils.isEmpty(stringPropertyValue) && !"0".equals(stringPropertyValue)){
					whf.framework.meta.entity.Object obj = MetaManager.getDefaultInstance().findByEntityClass(propertyType.getName());
					Service service = BeanFactory.getService(obj.getServiceName());
					BeanUtils.setProperty(result, property, service.findByPrimaryKey(Long.parseLong(stringPropertyValue)));
				}
			} else if(TypeUtils.isImplementsFrom(propertyType, Collection.class )){
				Collection<Entity> propertyCollection = (Collection<Entity>)BeanUtils.getProperty(result, property);
				Element propertyElement = xmlElement.getChild(property);
				if(propertyElement == null) continue;
				List children = propertyElement.getChildren("ele");
				for(int i=0; children != null && i < children.size(); i++){
					Element child = (Element)children.get(i);
					Class childType = Utils.getClassByClassName(child.getAttributeValue("type"));
					String refValue = child.getAttributeValue("ref");

					whf.framework.meta.entity.Object obj = MetaManager.getDefaultInstance().findByEntityClass(childType.getName());
					Service service = BeanFactory.getService(obj.getServiceName());
					Entity entity = service.findByPrimaryKey(Long.parseLong(refValue));
					propertyCollection.add(entity);
				}
			} else {
				BeanUtils.setProperty(result, property, stringPropertyValue);
			}
		}
		return result;
	}
	
	/** 
	 * 将对象转换为jdom节点
	 * @param input	输入对象
	 * @param properties	需要序列化到xml中的属性,缺省为所有属性
	 * @return
	 */
	public static Element toXmlElement(Object input, String[] properties) throws PropertyException{
		assert input != null: "The input object can not be null!";
		Element root = new Element("entity");
		root.setAttribute("class", input.getClass().getName());
		if(input instanceof Entity) {
			root.setAttribute("id", String.valueOf(((Entity)input).getId()));
		}
		PropertyDescriptor[] descriptorsArr = BeanUtils.getPropertyDescriptors(input.getClass());
		List<PropertyDescriptor> descriptors = Utils.newArrayList();
		if(properties != null) {
			for(PropertyDescriptor pd: descriptorsArr) {
				for(String property: properties) {
					if(StringUtils.equals(property, pd.getName())) {
						descriptors.add(pd);
						break;
					}
				}
			}
		} else {
			for(PropertyDescriptor pd: descriptorsArr) {
				descriptors.add(pd);
			}
		}
		for(PropertyDescriptor desc: descriptors) {
			if(input instanceof Entity && BeanUtils.hasProperty(AbstractStrutsForm.class, desc.getName())) continue;
			Class propertyType = desc.getPropertyType();
			String propertyName = desc.getName();
			Element propertyElement = new Element("property");
			propertyElement.setAttribute("name", propertyName);
			propertyElement.setAttribute("type", propertyType.getName());
			Object val = BeanUtils.getProperty(input, propertyName);
			
			if(TypeUtils.isImplementsFrom(propertyType, Entity.class)){	//业务对象属性使用ref标签
				Element refElement = new Element("many-to-one");
				refElement.setAttribute("type", propertyType.getName());
				if(val != null)
					if(val instanceof Entity) {
						long boid = val == null?0: ((Entity)val).getId();
						refElement.setAttribute("id", String.valueOf(boid));
					} else {
						refElement.setText(val.toString());
					}
				propertyElement.addContent(refElement);
			} else if(TypeUtils.isImplementsFrom(propertyType, java.util.List.class)){		//集合属性使用list标签
				Element listElement = new Element("list");
				if(val != null){
					java.util.List coll = (java.util.List)val;
					for(Iterator it = coll.iterator(); it.hasNext(); ){
						Object e = it.next();
						if(e instanceof Entity) {
							listElement.addContent(((Entity)e).toXmlElement());
						} else {
							Element refElement = new Element("entry");
							refElement.setAttribute("type", e.getClass().getName());
							refElement.setText(e.toString());
							listElement.addContent(refElement);
						}
					}
				}
				propertyElement.addContent(listElement);
			} else if(TypeUtils.isImplementsFrom(propertyType, java.util.Set.class)) {
				Element setElement = new Element("set");
				if(val != null){
					java.util.Set coll = (java.util.Set)val;
					for(Iterator it = coll.iterator(); it.hasNext(); ){
						Object e = it.next();
						if(e instanceof Entity) {
							setElement.addContent(((Entity)e).toXmlElement());
						} else {
							Element refElement = new Element("entry");
							refElement.setAttribute("type", e.getClass().getName());
							refElement.setText(e.toString());
							setElement.addContent(refElement);
						}
					}
				}
				propertyElement.addContent(setElement);
			} else if(TypeUtils.isImplementsFrom(desc.getPropertyType(), Map.class)){
				Element mapElement = new Element("map");
				if(val != null){
					Map map = (Map)val;
					for(Iterator it = map.keySet().iterator(); it.hasNext(); ){
						Object key = it.next();
						Object value = map.get(key);
						Element entry = new Element("entry");
						if(key instanceof Entity){
							entry.setAttribute("keyType", key.getClass().getName());
							entry.setAttribute("key", String.valueOf(((Entity)key).getId()));
						} else {
							entry.setAttribute("key", key.toString());
						}
						if(value instanceof Entity){
							if(value != null) {
								entry.setAttribute("valueType", String.valueOf(((Entity)value).getId()));
								entry.setAttribute("value", String.valueOf(((Entity)value).getId()));
							}
						} else {
							if(value != null)
								entry.setText(value.toString());
						}
						mapElement.addContent(entry);
					}
				}
				propertyElement.addContent(mapElement);
			} else if(desc.getPropertyType() == java.sql.Date.class){
				Element valueElement = new Element("value");
				valueElement.setText(val == null? "": DateUtils.formatDate((java.sql.Date)val));
				propertyElement.addContent(valueElement);
			} else if(TypeUtils.isImplementsFrom(desc.getPropertyType(), java.util.Date.class)){
				Element valueElement = new Element("value");
				valueElement.setText(val == null? "": DateUtils.formatDate((java.util.Date)val, "yyyy-MM-dd HH:mm:ss"));
				propertyElement.addContent(valueElement);
			} else {		//其它使用value标签
				Element valueElement = new Element("value");
				if(val == null){
					valueElement.setText("");
				} else {
					if(val instanceof java.util.Date){
						valueElement.setText(String.valueOf(((java.util.Date)val).getTime()));
					} else {
						valueElement.setText(val.toString());
					}
				}
				propertyElement.addContent(valueElement);
			}
			root.addContent(propertyElement);
		}
		return root;
	}
	
	public static String toXmlString(Object obj, String[] properties) throws PropertyException{
		Element ele = toXmlElement(obj, properties);
		XMLOutputter out = new XMLOutputter();
		return out.outputString(ele);
	}
	
}
